home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Aminet 5
/
Aminet 5 - March 1995.iso
/
Aminet
/
text
/
misc
/
pcal_4_5.lha
/
pcal
/
writefil.c
< prev
Wrap
C/C++ Source or Header
|
1994-10-16
|
37KB
|
1,425 lines
/*
* writefil.c - Pcal routines concerned with writing the PostScript output
*
* Contents:
*
* def_footstring
* expand_fmt
* find_daytext
* find_noteboxes
* print_colors
* print_dates
* print_db_word
* print_julian_info
* print_month
* print_moon_info
* print_pstext
* print_text
* print_word
* select_color
* set_rgb
* write_calfile
* write_psfile
*
* Revision history:
*
* 4.5 AWR 04/05/94 select real vs. dummy PostScript code
* within write_psfile() (cf. pcalinit.ps)
*
* 11/30/93 pre-scale all fonts used (as suggested
* by Andrew Houghton; cf. pcalinit.ps)
*
* 11/24/93 replace find_holidays() with
* print_colors() (cf. drawnums{} in
* pcalinit.ps)
*
* 11/16/93 Add set_rgb() to handle red:green:blue
* values for date/fill colors
*
* 09/23/93 Support both ROMAN-8 and LATIN-8 font
* mappings
*
* AWR 07/09/93 Revised PostScript comment block
*
* AWR 03/01/93 add optional mapping of 8-bit fonts
*
* AWR 02/05/93 Support -# flag (multiple copies of
* each output page)
*
* AWR 04/22/92 use STRSIZ for temp buffer size
*
* 4.4 AWR 04/07/92 revise to use new PUTCHAR and PUTSTR
* macros (cf. pcaldefs.h)
*
* 01/20/92 support -z and revised -[bgGO] flags
*
* AWR 01/13/92 support optional font size in -d and
* -t flags; move initialization of fonts
* and sizes here (from pcalinit.ps)
*
* 4.3 AWR 12/03/91 add support for -s flag (specify
* alternate date/fill box shading values)
*
* 4.2 AWR 10/08/91 add support for -[kK] flags (change
* position of small calendars)
*
* 10/03/91 add find_noteboxes(); revise to print
* text in multiple notes boxes
*
* add support for -S flag
*
* 10/02/91 modify def_footstring() to handle all
* types of strings; use it to print notes
* header (-N flag)
*
* 09/19/91 add write_calfile(), print_dates(),
* and new print_text() to generate
* input for Un*x "calendar" utility;
* renamed old print_text() as
* print_pstext() for clarity; revised
* to simplify setting working date
*
* 4.11 AWR 08/23/91 revise expand_fmt() to write results
* to string instead of stdout; revise
* print_word() to avoid writing null
* strings
*
* AWR 08/21/91 use ABBR_DAY_LEN and ABBR_MONTH_LEN
* (cf. pcallang.h) to print abbreviated
* day/month names
*
* AWR 08/21/91 add %u and %w (calculate week number
* so that 1/1 is always week 1); support
* %[+-]<n>[DWMY] to adjust working date
* by +|- <n> days/weeks/months/years
*
* 4.1 AWR 08/16/91 Support -G flag (outlined gray dates)
*
* 4.02 AWR 07/02/91 Added "%" expansions in text strings
* (cf. expand_fmt())
*
* 4.0 AWR 01/28/91 Support -B, -w flags and moon file
*
* 01/15/91 Extracted from pcal.c
*
*/
/*
* Standard headers:
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
/*
* Pcal-specific definitions:
*/
#include "pcaldefs.h"
#include "pcalglob.h"
#define WRITEFIL /* to get ordinal_suffix() from pcallang.h */
#include "pcallang.h"
#include "pcalinit.h" /* PostScript boilerplate */
/*
* Macros:
*/
/* make sure PRT() doesn't round "ph" up to 1.0 when printing it */
#define PRT_TWEAK(ph) ((ph) >= 0.9995 ? 0.0 : (ph))
/* advance working date by n days */
#define SET_DATE(n) do { \
MAKE_DATE(date, work_month, work_day + (n), work_year); \
normalize(&date); \
work_month = date.mm, work_day = date.dd, work_year = date.yy; \
} while (0)
/* prescale a font and add its name to list */
#define ADDFONT(name, size, font, isarray) do { \
char *p = alloc(strlen(name) + strlen(size) + 2); \
sprintf(allfonts[nfonts++] = p, "%s_%s", name, size); \
PRT("/%s { %sfontsize ", p, font); \
if (isarray) PRT("%s get ", size); \
PRT("%sfont FontFind } def\n", font); \
} while (0)
#define GEN_PSCODE(arr) \
for (ap = arr; *ap; ap++) \
PRT("%s\n", *ap)
#define FOOTSTRINGS() (lfoot[0] || cfoot[0] || rfoot[0])
/* various PostScript comments and commands for start/end of page */
#ifdef EPS
/* macro definitions for generating EPS output */
#define PS_STARTPAGE() do { \
page++; \
PRT("%%%%Page: %d %d\n", page, page); \
PRT("%%%%BeginPageSetup\n"); \
PRT("clear flush\n"); \
PRT("/PageNum { %d } def\n", page); \
PRT("/PageState save def\n"); \
PRT("%%%%EndPageSetup\n"); \
} while (0)
#define PS_ENDPAGE() do { \
PRT("%%%%PageTrailer\n"); \
PRT("showpage\n"); \
PRT("clear flush\n"); \
PRT("PageState restore\n"); \
} while (0)
#define PS_TRAILER() do { \
PRT("%%%%Trailer\n"); \
PRT("clear flush\n"); \
PRT("%%%%EOF\n"); \
} while (0)
#else /* non-EPS flavors of above macros */
#define PS_STARTPAGE()
#define PS_ENDPAGE() PRT("showpage\n");
#define PS_TRAILER()
#endif
/* reset working date to original date */
#define RESET_DATE() \
work_month = this_month, work_day = this_day, work_year = this_year
#define NEWFONT "-8" /* suffix for new 8-bit fonts */
#define MAXFONT 20 /* maximum number of fonts to prescale */
/*
* Globals:
*/
/* order of following strings must conform to #define's in pcaldefs.h (q.v.) */
static char *cond[3] = {"false", "true", "(some)"};
static int this_day, this_month, this_year; /* current day */
static int work_day, work_month, work_year; /* working day (cf. expand_fmt()) */
static char *kw_note, *kw_opt, *kw_year; /* keywords for -c output */
static int debug_text; /* generate debug output */
/*
* write_psfile - write PostScript code
*
* The actual output of the PostScript code is straightforward. This routine
* writes a PostScript header followed by declarations of all the PostScript
* variables affected by command-line flags and/or language dependencies. It
* the generates the PostScript boilerplate generated from pcalinit.ps, and
* finally calls print_month() to generate the PostScript code for each
* requested month.
*
*/
void
#ifdef PROTOS
write_psfile(int month, /* starting month */
int year, /* starting year */
int nmonths) /* number of months */
#else
write_psfile(month, year, nmonths)
int month; /* starting month */
int year; /* starting year */
int nmonths; /* number of months */
#endif
{
int i, nfonts, nfsize, copies, dfltsize, color_dates;
char *p, **ap, tmp[STRSIZ], alt_color, *allfonts[MAXFONT];
struct tm *p_tm;
time_t t;
#ifdef UN_X
struct passwd *pw;
#endif
/* default date, title, weekday font sizes (small/medium/large) */
static int dsize[3] = DATEFONTSIZE;
static int tsize[3] = TITLEFONTSIZE;
static int wsize[3] = WEEKDAYFONTSIZE;
static int fsize[3] = FOOTFONTSIZE;
static int dmargin[3] = DATEMARGIN;
static double gwidth[3] = GRIDLINEWIDTH;
static char *calsize[3] = CALSIZE;
debug_text = DEBUG(DEBUG_TEXT); /* debug text output? */
/*
* Write out PostScript prolog (including version/datefile stamp)
*/
/* comment block at top */
PRT("%%!%s\n", PS_RELEASE); /* PostScript release */
PRT("%%%%CreationDate: "); /* date/time stamp */
p_tm = localtime((time(&t), &t));
if (date_style == EUR_DATES)
PRT("%d.%d.", p_tm->tm_mday, p_tm->tm_mon + 1);
else
PRT("%d/%d/", p_tm->tm_mon + 1, p_tm->tm_mday);
PRT("%02d %02d:%02d:%02d\n", p_tm->tm_year % 100, p_tm->tm_hour,
p_tm->tm_min, p_tm->tm_sec);
PRT("%%%%Creator: %s %s %s\n", CREATED_MSG, progname, version);
#ifdef UN_X
if ((pw = getpwuid(getuid())) != NULL) {
strcpy(tmp, pw->pw_gecos);
if ((p = strchr(tmp, ',')) != NULL)
*p = '\0';
PRT("%%%%For: %s\n", pw->pw_name);
PRT("%%%%Routing: %s\n", tmp);
}
#endif
/* Identify the output (month/year range and input file) */
if (do_whole_year && month == JAN) {
PRT("%%%%Title: %s %d", TITLE_MSG, year);
if (nmonths > 12)
PRT(" - %d", year + ((nmonths - 1) / 12));
} else {
char c = date_style == EUR_DATES ? '.' : '/';
PRT("%%%%Title: %s %d%c%02d", TITLE_MSG, month, c, year % 100);
if (nmonths > 1)
PRT(" - %d%c%02d", (month + (nmonths - 1) - 1) % 12 + 1,
c, (year + (nmonths - 1 + month - 1) / 12) % 100);
}
if (*datefile)
PRT(DATEFILE_MSG, datefile);
PRT("\n");
/* Miscellaneous other identification */
PRT("%%%%Pages: %d\n", do_whole_year ? nmonths / 12 : nmonths);
PRT("%%%%PageOrder: Ascend\n");
PRT("%%%%Orientation: %s\n", rotate == LANDSCAPE ? "Landscape" :
"Portrait");
PRT("%%%%BoundingBox: 0 0 612 792\n");
PRT("%%%%ProofMode: NotifyMe\n");
PRT("%%%%EndComments\n\n");
/* number of copies (from -#<n> flag) */
if ((copies = atoi(ncopy)) > MAXCOPY)
copies = MAXCOPY; /* save a tree */
if (copies > 1)
PRT("/#copies %d def\n", copies);
/* calendar sizes: to minimize number of pre-scaled fonts, whole-
* year calendars define 'medium' as 0 and the other sizes as -1
* (not used); single-month calendars define 'large' as 0, 'small'
* as 1, and 'medium' as -1 (not used)
*/
for (i = SMALL; i <= LARGE; i++)
PRT("/%s %d def\n", calsize[i],
do_whole_year ? (i == MEDIUM ? 0 : -1) :
(i == MEDIUM ? -1 : i == SMALL));
/* font names and sizes */
/* v4.4 supports user override of note and large date/title sizes */
nfsize = (p = strrchr(notesfont, '/')) ? *p++ = '\0', atoi(p) :
atoi(strrchr(NOTESFONT, '/') + 1);
dsize[LARGE] = (p = strrchr(datefont, '/')) ? *p++ = '\0', atoi(p) :
atoi(strrchr(DATEFONT, '/') + 1);
tsize[LARGE] = (p = strrchr(titlefont, '/')) ? *p++ = '\0', atoi(p) :
atoi(strrchr(TITLEFONT, '/') + 1);
/* enlarge footer strings in whole-year/portrait mode */
if (do_whole_year && rotate == PORTRAIT)
fsize[MEDIUM] *= 1.25;
/*
* if 8-bit remapping has been requested (-r flag), create new fonts
* with desired character remapping (Roman8 or Latin1, as requested)
*/
if (mapfonts != NOMAP) {
/* include desired mapping */
GEN_PSCODE(mapfonts == ROMAN8 ? ps_roman8 : ps_latin1);
/* boilerplate to remap for 8-bit fonts */
GEN_PSCODE(ps_remap);
/* always generate code to remap title font */
PRT("/%s /%s%s remap_font\n", titlefont, titlefont, NEWFONT);
strcat(titlefont, NEWFONT);
#if MAP_DATEFONT /* any text printed in date font (cf. pcaldefs.h)? */
/* generate code to remap date font if necessary */
PRT("FontDirectory /%s%s known not {\n", datefont, NEWFONT);
PRT("/%s /%s%s remap_font\n", datefont, datefont, NEWFONT);
PRT("} if\n");
strcat(datefont, NEWFONT);
#endif
/* generate code to remap notes font if necessary */
PRT("FontDirectory /%s%s known not {\n", notesfont, NEWFONT);
PRT("/%s /%s%s remap_font\n", notesfont, notesfont, NEWFONT);
PRT("} if\n");
strcat(notesfont, NEWFONT);
}
/* define title, notes, and date fonts */
PRT("/titlefont /%s def\n", titlefont);
PRT("/datefont /%s def\n", datefont);
PRT("/notesfont /%s def\n", notesfont);
/* typically defined in terms of above fonts - must define last */
PRT("/weekdayfont %s def\n", WEEKDAYFONT);
PRT("/footfont %s def\n", FOOTFONT);
PRT("/headingfont %s def\n", HEADINGFONT);
/* print various font sizes and line/margin widths as PostScript arrays:
* one element for whole-year calendars; two (large, small) for single-
* month calendars
*/
if (do_whole_year) {
PRT("/datemargin [ %d ] def\n", dmargin[MEDIUM]);
PRT("/gridlinewidth [ %.1f ] def\n", gwidth[MEDIUM]);
PRT("/titlefontsize [ %d ] def\n", tsize[MEDIUM]);
PRT("/datefontsize [ %d ] def\n", dsize[MEDIUM]);
} else {
PRT("/datemargin [ %d %d ] def\n", dmargin[LARGE], dmargin[SMALL]);
PRT("/gridlinewidth [ %.1f %.1f ] def\n", gwidth[LARGE], gwidth[SMALL]);
PRT("/titlefontsize [ %d %d ] def\n", tsize[LARGE], tsize[SMALL]);
PRT("/datefontsize [ %d %d ] def\n", dsize[LARGE], dsize[SMALL]);
}
dfltsize = do_whole_year ? MEDIUM : LARGE;
PRT("/weekdayfontsize %d def\n", wsize[dfltsize]);
PRT("/footfontsize %d def\n", fsize[dfltsize]);
PRT("/notesfontsize %d def\n", nfsize);
PRT("/headingfontsize %d def\n", HEADINGFONTSIZE);
/* pre-scale all fonts used by PostScript code; try to be smart about
* skipping those that we know (at this point) won't be needed (whole-
* year calendars use either 3 or 4 fonts, while single-month calendars
* can use anywhere from 3 to 8). "FF" et. al. are indices into the
* font array (cf pcalinit.ps) for the different font types.
*/
PRT("/FontFind { findfont exch scalefont } def\n");
nfonts = 0;
PRT("/FF %d def\n", nfonts); /* footers */
if (FOOTSTRINGS())
ADDFONT("ff", calsize[dfltsize], "foot", FALSE);
if (do_whole_year) {
PRT("/TF %d def\n", nfonts); /* month/year title */
ADDFONT("tf", calsize[MEDIUM], "title", TRUE);
PRT("/DF %d def\n", nfonts); /* dates */
ADDFONT("df", calsize[MEDIUM], "date", TRUE);
} else {
PRT("/HF %d def\n", nfonts); /* 'Notes' heading */
if (notes_hdr[0])
ADDFONT("hf", calsize[LARGE], "heading", FALSE);
/* large/small (if used) scalings of the same font must be
* contiguous and appear in that order
*/
PRT("/TF %d def\n", nfonts); /* large/small title */
ADDFONT("tf", calsize[LARGE], "title", TRUE);
if (small_cal_pos != SC_NONE)
ADDFONT("tf", calsize[SMALL], "title", TRUE);
PRT("/DF %d def\n", nfonts); /* large/small dates */
ADDFONT("df", calsize[LARGE], "date", TRUE);
if (small_cal_pos != SC_NONE)
ADDFONT("df", calsize[SMALL], "date", TRUE);
PRT("/NF %d def\n", nfonts); /* text in boxes */
ADDFONT("nf", calsize[LARGE], "notes", FALSE);
}
PRT("/WF %d def\n", nfonts); /* weekdays */
ADDFONT("wf", calsize[dfltsize], "weekday", FALSE);
/* generate the font array (automatically in sync with above) */
PRT("/allfonts [\n\t");
for (i = 0; i < nfonts; i++) {
PRT("%s ", allfonts[i]);
free(allfonts[i]);
}
PRT("\n] def\n");
/*
* define various strings and numeric values used by Pcal
*/
/* month names */
PRT("/month_names [");
for (i = JAN; i <= DEC; i++) {
PRT(i % 6 == 1 ? "\n\t" : " ");
(void) print_word(months[i-1]);
}
PRT(" ] def\n");
/* day names - abbreviate if printing entire year on page */
PRT("/day_names [");
for (i = SUN; i <= SAT; i++) {
PRT(i % 6 == 0 && ! do_whole_year ? "\n\t" : " ");
strcpy(tmp, days[(i + first_day_of_week) % 7]);
if (do_whole_year)
tmp[ABBR_DAY_LEN] = '\0';
(void) print_word(tmp);
}
PRT(" ] def\n");
/* line separator */
PRT("/linesep ");
print_word(LINE_SEP);
PRT(" def\n");
/* rotation, scale, and translate values */
PRT("/rval %d def\n", rotate);
PRT("/xsval %s def\n/ysval %s def\n", xsval, ysval);
PRT("/xtval %s def\n/ytval %s def\n", xtval, ytval);
/* moon, Julian date, and box fill flags */
PRT("/draw-moons %s def\n", cond[draw_moons]);
PRT("/julian-dates %s def\n", cond[julian_dates]);
PRT("/fill-boxes %s def\n", cond[! blank_boxes]);
/* position of small calendars */
PRT("/prev_small_cal %d def\n", prev_cal_box[small_cal_pos]);
PRT("/next_small_cal %d def\n", next_cal_box[small_cal_pos]);
/* date and fill box shading values */
strcpy(tmp, shading);
*(p = strchr(tmp, '/')) = '\0';
PRT("/dategray %s def\n", set_rgb(tmp));
PRT("/fillgray %s def\n", set_rgb(p + 1));
color_dates = strchr(tmp, RGB_CHAR) != NULL;
/* PostScript boilerplate (part 1 of 1) */
GEN_PSCODE(header);
/* Additional PostScript code tailored to this calendar */
GEN_PSCODE(color_dates ?
ps_prtday_rgb : /* color prtday{} */
ps_prtday_bw); /* B&W prtday{} */
GEN_PSCODE(FOOTSTRINGS() ?
ps_footer : /* at least one foot string */
ps_nofooter); /* no foot strings */
GEN_PSCODE(blank_boxes ?
ps_nofill : /* blank fill boxes */
ps_fill); /* shaded fill boxes */
if (do_whole_year) {
GEN_PSCODE(rotate == LANDSCAPE ?
ps_year_l : /* medium months (landscape) */
ps_year_p); /* medium months (portrait) */
GEN_PSCODE(ps_nojulians); /* no julians or moons */
GEN_PSCODE(ps_nomoons);
} else {
GEN_PSCODE(julian_dates == NO_JULIANS ?
ps_nojulians : /* no julian dates */
ps_julians); /* some julian dates */
GEN_PSCODE(draw_moons == NO_MOONS ?
ps_nomoons : /* no moons */
ps_moons); /* some or all moons */
if (head)
GEN_PSCODE(ps_text); /* date text */
GEN_PSCODE(ps_month); /* single month */
}
/*
* Write out PostScript code to print calendars
*/
for (this_month = month, this_year = year; nmonths--; ) {
print_month(this_month, this_year);
this_year = NEXT_YEAR(this_month, this_year);
this_month = NEXT_MONTH(this_month, this_year);
}
/* generate trailer at end of PostScript output */
PS_TRAILER();
}
/*
* write_calfile - write dates in format suitable for Un*x "calendar" utility
* (and subsequent use by Pcal)
*/
void
#ifdef PROTOS
write_calfile(int month, /* starting month */
int year, /* starting year */
int nmonths) /* number of months */
#else
write_calfile(month, year, nmonths)
int month /* starting month */;
int year; /* starting year */
int nmonths; /* number of months */
#endif
{
KWD *k;
/* look up the Pcal keywords (assumed present) for the -c output file */
for (k = keywds; k->name; k++) {
if (k->code == DT_NOTE) kw_note = k->name;
if (k->code == DT_OPT) kw_opt = k->name;
if (k->code == DT_YEAR) kw_year = k->name;
}
/* print the date style for subsequent use by Pcal */
PRT("%s -%c\n", kw_opt, date_style == USA_DATES ? F_USA_DATES :
F_EUR_DATES);
for (this_month = month, this_year = year; nmonths--; ) {
print_dates(this_month, this_year);
this_year = NEXT_YEAR(this_month, this_year);
this_month = NEXT_MONTH(this_month, this_year);
}
}
/*
* low-level utilities for PostScript generation
*/
/*
* set_rgb - convert "<r>:<g>:<b>" to [r g b] or "<gray>" to [gray];
* return pointer to static buffer containing converted string
*/
char *
#ifdef PROTOS
set_rgb(char *s)
#else
set_rgb(s)
char *s;
#endif
{
static char buf[STRSIZ];
char *p1, *p2;
double val[3];
int n;
val[0] = val[1] = val[2] = 0; /* defaults */
/* extract 1 - 3 floating-point values from string */
for (n = 1, p1 = s; n <= 3; n++, p1 = p2 + 1) {
val[n-1] = atof(p1);
if ((p2 = strchr(p1, RGB_CHAR)) == NULL)
break;
}
/* single value is gray scale; assume anything else is [r g b] */
if (n > 1)
sprintf(buf, "[%.3f %.3f %.3f]", val[0], val[1], val[2]);
else
sprintf(buf, "[%.3f]", val[0]);
return buf;
}
/*
* select_color - if the holiday color has not been explicitly selected,
* choose a color which contrasts with the majority of weekday colors
*/
int
#ifdef PROTOS
select_color(void)
#else
select_color()
#endif
{
int i, min, index;
char count[NUM_COLORS];
for (i = 0; i < NUM_COLORS; i++) /* clear counts */
count[i] = 0;
for (i = SUN; i <= SAT; i++) /* count colors */
count[day_color[i]]++;
/* find smallest non-zero count; set its index and value */
for (i = 0, min = 99; i < NUM_COLORS; i++)
if (count[i] && count[i] < min)
min = count[index = i];
/* return least-used color (or pick one if only one color used) */
return min == 7 ? index == BLACK ? GRAY : BLACK : index;
}
/*
* expand_fmt - expand a strftime-like date format specifier; pcal supports
* %[aAbBdjmUWyY] from strftime() plus %[luwDM] and prefixes [0o+-] (see
* below); places expanded string in output buffer and returns pointer to
* character following end of format specifier. Assumes working date has
* been initialized (via RESET_DATE() macro) prior to first call for a given
* text string
*/
char *
#ifdef PROTOS
expand_fmt(char *buf, /* output buffer (filled in) */
char *p) /* character following percent sign */
#else
expand_fmt(buf, p)
char *buf; /* output buffer (filled in) */
char *p; /* character following percent sign */
#endif
{
char c;
static char *prefixes = "0o+-";
int firstday, wkday;
int adjust = 0, print_lz = FALSE, ordinal = FALSE, prev_num = -1;
int num_present = FALSE, num_value = 0;
DATE date;
/* For compatibility with version 4.1, still support %[+-][bBdmY]
* (print the next/last month-name/day/month/year). Version 4.11
* introduces %[+-]<n>[DWMY], which adjusts the working date by
* [+-]<n> days/weeks/months/years; this method is preferred due
* to its greater flexibility.
*/
buf[0] = '\0'; /* initialize output to null string */
do { /* loop until format character found */
switch (c = *p++) {
case 'a': /* %a : abbreviated weekday */
case 'A': /* %A : full weekday */
wkday = calc_weekday(work_month, work_day, work_year);
strcpy(buf, days[wkday]);
if (c == 'a')
buf[ABBR_DAY_LEN] = '\0';
break;
case 'b': /* %b : abbreviated month name */
case 'B': /* %B : full month name */
strcpy(buf, months[(work_month + adjust + 11) % 12]);
if (c == 'b')
buf[ABBR_MONTH_LEN] = '\0';
break;
case 'd': /* %d : day of month (01-31) */
prev_num = work_day;
sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
break;
case 'D': /* %D : adjust working date by <N> days (NEW) */
if (!num_present || num_value == 0)
RESET_DATE();
else
SET_DATE(adjust * num_value);
break;
case 'j': /* %j : day of year (001-366) */
prev_num = DAY_OF_YEAR(work_month, work_day,
work_year);
sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
break;
case 'l': /* %l : days left in year (000-365) (NEW) */
prev_num = YEAR_LEN(work_year) - DAY_OF_YEAR(work_month,
work_day, work_year);
sprintf(buf, print_lz ? "%03d" : "%d", prev_num);
break;
case 'm': /* %m : month (01-12) */
prev_num = (work_month + adjust + 11) % 12 + 1;
sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
break;
case 'M': /* %M : adjust date by <N> months (NEW) */
if (!num_present || num_value == 0)
RESET_DATE();
else {
int len;
work_month += adjust * num_value;
while (work_month > DEC) {
work_month -= 12;
work_year++;
}
while (work_month < JAN) {
work_month += 12;
work_year--;
}
/* make sure day of new month is legal */
len = LENGTH_OF(work_month, work_year);
if (work_day > len)
work_day = len;
}
break;
/* %u considers the week containing 1/1 to be week 1 and
* the next "logical Sunday" (the first day of the week
* as printed - cf. the -F option) to be the start of week
* 2; %U considers the first "logical Sunday" of the year to
* be the start of week 1. %w and %W behave like %u and %U
* respectively, but use the first "logical Monday" instead.
*/
case 'W': /* %W : week number (00-53) */
/* %W, if prefaced by [+-]N, adjusts the date by
* [+-]N weeks (resets if N == 0); check for this
* case first
*/
if (num_present) {
if (num_value == 0) /* N = 0: reset date */
RESET_DATE();
else
SET_DATE(7 * adjust * num_value);
break;
}
/* fall through */
case 'u': /* %u : week number (01-54) (NEW) */
case 'U': /* %U : week number (00-53) */
case 'w': /* %w : week number (01-54) (NEW) */
firstday = ((TOLOWER(c) == 'w' ? 15 : 14) -
START_BOX(JAN, work_year)) % 7 + 1;
prev_num = (DAY_OF_YEAR(work_month, work_day,
work_year) - firstday + 7) / 7;
if (islower(c) && firstday != 1)
prev_num++;
sprintf(buf, print_lz ? "%02d" : "%d", prev_num);
break;
case 'y': /* %y : year w/o century (00-99) */
prev_num = (work_year + adjust) % 100;
sprintf(buf, "%02d", prev_num);
break;
case 'Y': /* %Y : year w/century */
/* %Y, if prefaced by [+-]N, adjusts the date by
* [+-]N years (resets if N == 0); check for this
* case first
*/
if (num_present) {
if (num_value == 0) /* N = 0: reset date */
RESET_DATE();
else {
int len;
work_year += adjust * num_value;
/* make sure day is legal */
len = LENGTH_OF(work_month, work_year);
if (work_day > len)
work_day = len;
}
} else {
prev_num = work_year + adjust;
sprintf(buf, "%d", prev_num);
}
break;
/* prefix flags [o0+-] : set flags for next pass */
case 'o': /* %o : ordinal suffix (NEW) */
ordinal = TRUE;
break;
case '0': /* %0 : add leading zeroes (NEW) */
print_lz = TRUE;
break;
case '+': /* %+ : increment next value (NEW) */
case '-': /* %- : decrement next value (NEW) */
adjust = c == '-' ? -1 : 1;
if (isdigit(*p)) { /* get the number */
num_present = TRUE;
while (isdigit(*p))
num_value = num_value * 10 +
(*p++ - '0');
}
break;
case '\0': /* accidental end-of-string */
case ' ':
return p - 1;
default: /* other - just copy it to output */
sprintf(buf, "%c", c);
break;
};
} while (strchr(prefixes, c) != NULL);
/* append ordinal suffix if requested */
if (ordinal && prev_num >= 0)
strcat(buf, ordinal_suffix(prev_num));
return p;
}
/*
* print_word - print a single word, representing punctuation and non-ASCII
* characters as octal literals and expanding format specifiers; return pointer
* to character following word (NULL if no word follows)
*/
char *
#ifdef PROTOS
print_word(char *p)
#else
print_word(p)
char *p;
#endif
{
char c, buf[STRSIZ];
int first = TRUE; /* flag to avoid printing null strings */
if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0')
return NULL;
while ((c = *p) && !isspace(c & CHAR_MSK)) {
if (c == '%' && p[1] != '\0') {
p = expand_fmt(buf, p + 1);
if (*buf && first) {
PRT("(");
first = FALSE;
}
PUTSTR(isalnum, buf, stdout);
} else {
if (first)
PRT("(");
first = FALSE;
PUTCHAR(isalnum, c, stdout);
p++;
}
}
if (!first)
PRT(")");
return p;
}
/*
* print_db_word - debug version of print_word; omits parentheses, does not
* convert punctuation to escape sequences, and writes results to stderr
* (not stdout)
*/
char *
#ifdef PROTOS
print_db_word(char *p)
#else
print_db_word(p)
char *p;
#endif
{
char c, buf[STRSIZ];
if (*p == '\0' || *(p += strspn(p, WHITESPACE)) == '\0')
return NULL;
while ((c = *p) && !isspace(c & CHAR_MSK)) {
if (c == '%' && p[1] != '\0') {
p = expand_fmt(buf, p + 1);
PUTSTR(isprint, buf, stderr);
} else {
PUTCHAR(isprint, c, stderr);
p++;
}
}
return p;
}
/*
* print_pstext - print tokens in text (assumed separated by single blank)
* in PostScript format and as debugging information if requested
*/
void
#ifdef PROTOS
print_pstext(char *p)
#else
print_pstext(p)
char *p;
#endif
{
char *s = p; /* save for possible second pass */
while (p = print_word(p))
PRT(" ");
/* repeat to generate debugging info if requested */
if (debug_text)
while (s = print_db_word(s))
FPR(stderr, " ");
}
/*
* print_text - print text as supplied; expand format specifiers but
* do not tokenize into words or translate punctuation to escape sequences
*/
void
#ifdef PROTOS
print_text(char *p)
#else
print_text(p)
char *p;
#endif
{
char c, buf[STRSIZ];
while (c = *p)
if (c == '%' && p[1] != '\0') {
p = expand_fmt(buf, p + 1);
PUTSTR(isprint, buf, stdout);
} else {
PUTCHAR(isprint, c, stdout);
p++;
}
}
/*
* def_footstring - print definition for foot string, again converting
* all characters other than letters, digits, or space to octal escape
* and expanding format specifiers
*/
void
#ifdef PROTOS
def_footstring(char *p, /* definition */
char *str) /* name of string */
#else
def_footstring(p, str)
char *p; /* definition */
char *str; /* name of string */
#endif
{
char c, buf[STRSIZ];
this_day = 1; /* set default day in foot string */
RESET_DATE(); /* reset working date */
PRT("/%s (", str);
while (c = *p)
if (c == '%' && p[1] != '\0') {
p = expand_fmt(buf, p + 1);
PUTSTR(isalnum, buf, stdout);
} else {
PUTCHAR(isalnum, c, stdout);
p++;
}
PRT(") def\n");
}
/*
* Routines to extract and print data
*/
/*
* find_daytext - find and print the day (including notes) or holiday text
* for the specified month/year
*/
void
#ifdef PROTOS
find_daytext(int month,
int year,
int is_holiday) /* TRUE: print holiday text */
#else
find_daytext(month, year, is_holiday)
int month, year;
int is_holiday; /* TRUE: print holiday text */
#endif
{
register int day;
year_info *py;
month_info *pm;
register day_info *pd;
int first;
char *fcn = is_holiday ? "holidaytext" : "daytext";
char hol = is_holiday ? '*' : ' ';
/* if no text for this year and month, return */
if ((py = find_year(year, FALSE)) == NULL ||
(pm = py->month[month-1]) == NULL)
return;
/* walk array of day text pointers and linked lists of text */
for (day = 1; day <= LAST_NOTE_DAY; day++) {
for (pd = pm->day[day-1], first = TRUE;
pd;
pd = pd->next) {
if (pd->is_holiday != is_holiday)
continue;
if (first) {
PRT("%d [ \n", day >= FIRST_NOTE_DAY ?
note_box(month, day, year) : day);
}
else {
PRT("\n");
print_word(LINE_SEP); /* separate lines */
PRT("\n");
}
this_day = day >= FIRST_NOTE_DAY ? 1 : day;
RESET_DATE(); /* reset working date */
if (debug_text)
if (day < FIRST_NOTE_DAY)
FPR(stderr, "%02d/%02d/%d%c ", month,
day, year, hol);
else
FPR(stderr, "%02d[%02d]%d ", month,
day - FIRST_NOTE_DAY + 1, year);
print_pstext(pd->text);
if (debug_text)
FPR(stderr, "\n");
first = FALSE;
}
if (! first) { /* wrap up call (if one made) */
PRT("\n] %s\n", day >= FIRST_NOTE_DAY ? "notetext" :
fcn);
}
}
}
/*
* print_colors - print array specifying color of each date in current month
* (formerly calculated on the fly in drawnums{} in pcalinit.ps)
*/
void
#ifdef PROTOS
print_colors(int month,
int year)
#else
print_colors(month, year)
int month, year;
#endif
{
register int day;
year_info *py;
month_info *pm;
unsigned long holidays;
int i, j, len;
short color[32];
len = LENGTH_OF(month, year);
/* determine each date's color from its day of the week */
for (day = 1, j = FIRST_OF(month, year);
day <= len;
day++, j = (j + 1) % 7) {
color[day] = day_color[j];
}
pm = (py = find_year(year, FALSE)) ? py->month[month-1] : NULL;
/* override weekday color for holidays */
for (holidays = pm ? pm->holidays : 0, day = 1;
holidays;
holidays >>= 1, day++)
if (holidays & 01)
color[day] = holiday_color;
PRT("/date_color [ -1"); /* dummy value for element 0 */
for (day = 1; day <= len; day++)
PRT("%s %d", day % 10 == 1 ? " " : "", color[day]);
PRT(" ] def\n");
}
/*
* find_noteboxes - find and print the note box numbers for specified
* month/year
*/
void
#ifdef PROTOS
find_noteboxes(int month,
int year)
#else
find_noteboxes(month, year)
int month, year;
#endif
{
register int day;
year_info *py;
month_info *pm;
/* if no text for this year and month, print empty list and return */
if ((py = find_year(year, FALSE)) == NULL ||
(pm = py->month[month-1]) == NULL) {
PRT("/noteboxes [ ] def\n");
return;
}
PRT("/noteboxes ["); /* start definition of list */
/* walk array of note text pointers, converting days to box numbers */
for (day = FIRST_NOTE_DAY; day <= LAST_NOTE_DAY; day++)
if (pm->day[day-1])
PRT(" %d", note_box(month, day, year));
PRT(" ] def\n");
}
/*
* print_dates - print the dates (in "calendar" format) for the specified
* month and year
*/
void
#ifdef PROTOS
print_dates(int month,
int year)
#else
print_dates(month, year)
int month, year;
#endif
{
register int day;
year_info *py;
month_info *pm;
register day_info *pd;
unsigned long holidays;
int has_holiday_text;
static int first = TRUE;
static int save_year = 0;
/* if no text for this year and month, return */
if ((py = find_year(year, FALSE)) == NULL ||
(pm = py->month[month-1]) == NULL)
return;
/* print the year if it has changed */
if (year != save_year)
PRT("%s %d\n", kw_year, save_year = year);
/* walk array of day text pointers and linked lists of text */
for (holidays = pm->holidays, day = 1;
day < FIRST_NOTE_DAY;
holidays >>= 1, day++) {
has_holiday_text = FALSE;
for (pd = pm->day[day-1]; pd; pd = pd->next) {
if (date_style == USA_DATES)
PRT("%02d/%02d", month, day);
else
PRT("%02d/%02d", day, month);
PRT(pd->is_holiday ? "* " : " ");
this_day = day;
RESET_DATE(); /* reset working date */
print_text(pd->text);
PRT("\n");
has_holiday_text |= pd->is_holiday;
}
/* was date flagged as holiday w/o associated text? */
if ((holidays & 01) && !has_holiday_text) {
if (date_style == USA_DATES)
PRT("%02d/%02d*\n", month, day);
else
PRT("%02d/%02d*\n", day, month);
}
}
}
/*
* print_moon_info - print the information necessary to draw moons. If
* printing moons on all days, print the phase for each day; if printing
* only quarter moons, tweak the phase to an exact quarter (so the icon
* is printed correctly) and generate a list of the quarter-moon dates
*/
void
#ifdef PROTOS
print_moon_info(int month,
int year)
#else
print_moon_info(month, year)
int month, year;
#endif
{
int n, ndays, day, quarter;
char *p;
unsigned long qdays;
double phase;
static char *q[4] = {"NM", "1Q", "FM", "3Q"};
if (draw_moons == NO_MOONS)
return;
/* print the phase of the moon for each day of the month */
PRT("/moon_phases [\t\t%% from ");
if ((p = find_moonfile(year)) != NULL)
PRT ("%s", p);
else {
PRT("algorithm");
if (atof(time_zone) != 0.0)
PRT(" (UTC offset = %s)", time_zone);
}
PRT("\n\t");
for (n = 0, qdays = 0L, day = 1, ndays = LENGTH_OF(month, year);
day <= ndays;
day++) {
phase = find_phase(month, day, year, &quarter);
if (DEBUG(DEBUG_MOON))
FPR(stderr, "%02d/%02d/%d: %.3f %s\n", month, day,
year, phase, quarter != MOON_OTHER ?
q[quarter] : "");
/* adjust phase to exact quarter if printing only quarters */
if (draw_moons == SOME_MOONS && quarter != MOON_OTHER)
phase = 0.25 * quarter;
if (draw_moons == ALL_MOONS || quarter != MOON_OTHER)
PRT("%.3f%s", PRT_TWEAK(phase), ++n % 10 == 0 ?
"\n\t" : " ");
if (quarter != MOON_OTHER)
qdays |= 1L << (day - 1);
}
PRT("] def\n");
/* if drawing only quarter moons, print days when they occur */
if (draw_moons == SOME_MOONS) {
PRT("/quarter_moons [ ");
for (day = 1; qdays; day++, qdays >>= 1)
if (qdays & 01)
PRT("%d ", day);
PRT("] def\n");
}
}
/*
* print_julian_info - print the information necessary to print Julian dates
*/
void
#ifdef PROTOS
print_julian_info(int month,
int year)
#else
print_julian_info(month, year)
int month, year;
#endif
{
if (julian_dates != NO_JULIANS)
PRT("/jdstart %d def\n", DAY_OF_YEAR(month, 1, year));
if (julian_dates == ALL_JULIANS)
PRT("/yearlen %d def\n", YEAR_LEN(year));
}
/*
* print_month - generate calendar for specified month/year
*/
void
#ifdef PROTOS
print_month(int month,
int year)
#else
print_month(month, year)
int month, year;
#endif
{
static int nmonths = 0, page = 0;
int startbox;
/* start of new physical page? */
if (!do_whole_year || nmonths % 12 == 0)
PS_STARTPAGE();
PRT("/year %d def\n", year); /* set up year and month */
PRT("/month %d def\n", month);
/* move starting box to second row if conflict with small calendars */
startbox = START_BOX(month, year);
if (!do_whole_year &&
(prev_cal_box[small_cal_pos] == startbox ||
next_cal_box[small_cal_pos] == startbox) )
startbox += 7;
PRT("/startbox %d def\n", startbox);
PRT("/ndays %d def\n", LENGTH_OF(month, year));
find_noteboxes(month, year); /* make list of note boxes */
print_colors(month, year); /* make list of date colors */
/* are we printing 12 months per page or only one? */
if (do_whole_year) {
/* reset foot strings at start of each page */
if (nmonths % 12 == 0) {
def_footstring(lfoot, "Lfootstring");
def_footstring(cfoot, "Cfootstring");
def_footstring(rfoot, "Rfootstring");
def_footstring(notes_hdr, "notesheading");
}
PRT("/posn %d def\n", nmonths % 12); /* location on page */
PRT("printmonth\n");
}
else {
/* reset foot strings each month (may change) */
def_footstring(lfoot, "Lfootstring");
def_footstring(cfoot, "Cfootstring");
def_footstring(rfoot, "Rfootstring");
def_footstring(notes_hdr, "notesheading");
/* generate information necessary for small calendars */
if (small_cal_pos != SC_NONE) {
int m, y;
PRT("/p_year %d def\n", y = PREV_YEAR(month, year));
PRT("/p_month %d def\n", m = PREV_MONTH(month, year));
PRT("/p_startbox %d def\n", START_BOX(m, y));
PRT("/p_ndays %d def\n", LENGTH_OF(m, y));
PRT("/n_year %d def\n", y = NEXT_YEAR(month, year));
PRT("/n_month %d def\n", m = NEXT_MONTH(month, year));
PRT("/n_startbox %d def\n", START_BOX(m, y));
PRT("/n_ndays %d def\n", LENGTH_OF(m, y));
}
print_julian_info(month, year); /* Julian date info */
print_moon_info(month, year); /* moon info */
PRT("printmonth\n");
find_daytext(month, year, TRUE); /* holiday text */
find_daytext(month, year, FALSE); /* day and note text */
}
/* end of physical page? */
if (!do_whole_year || ++nmonths % 12 == 0)
PS_ENDPAGE();
}